---
title: Built-in parsers
description: When using strings is not enough
---

import {
  DemoFallback,
  IntegerParserDemo,
  StringParserDemo,
  FloatParserDemo,
  HexParserDemo,
  IndexParserDemo,
  BooleanParserDemo,
  StringLiteralParserDemo,
  DateISOParserDemo,
  DatetimeISOParserDemo,
  DateTimestampParserDemo,
  JsonParserDemo,
  NativeArrayParserDemo
} from '@/content/docs/parsers/demos'

Search params are strings by default, but chances are your state is more complex than that.

You might want to use numbers, booleans, Dates, objects, arrays, or even custom types.
This is where **parsers** come in.

`nuqs` provides built-in parsers for the most common types, and allows you to [define your own](/docs/parsers/making-your-own).

## String

```ts
import { parseAsString } from 'nuqs'
```

<Suspense fallback={<DemoFallback />}>
  <StringParserDemo />
</Suspense>

<Callout title="Type-safety tip">
`parseAsString` is a noop: it does not perform any validation when parsing,
and will accept **any** value.

If you're expecting a certain set of string values, like `'foo' | 'bar'{:ts}`,
see [Literals](#literals) for ensuring type-runtime safety.

</Callout>

If search params are strings by default, what's the point of this _"parser"_ ?

It becomes useful if you're declaring a search params object, and/or you want
to use the builder pattern to specify [default values](/docs/basic-usage#default-values)
and [options](/docs/options):

```ts
export const searchParamsParsers = {
  q: parseAsString.withDefault('').withOptions({
    shallow: false
  })
}
```

## Numbers

### Integers

Transforms the search param string into an integer with `parseInt` (base 10).

```ts
import { parseAsInteger } from 'nuqs'

useQueryState('int', parseAsInteger.withDefault(0))
```

<Suspense fallback={<DemoFallback />}>
  <IntegerParserDemo />
</Suspense>

### Floating point

Same as integer, but uses `parseFloat` under the hood.

```ts
import { parseAsFloat } from 'nuqs'

useQueryState('float', parseAsFloat.withDefault(0))
```

<Suspense fallback={<DemoFallback />}>
  <FloatParserDemo />
</Suspense>

### Hexadecimal

Encodes integers in hexadecimal.

```ts
import { parseAsHex } from 'nuqs'

useQueryState('hex', parseAsHex.withDefault(0x00))
```

<Suspense fallback={<DemoFallback />}>
  <HexParserDemo />
</Suspense>

<Callout title="Going further">
  Check out the [Hex Colors](/playground/hex-colors) playground for a demo.
</Callout>

### Index

Same as integer, but adds a `+1` offset to the serialized querystring (and `-1` when parsing).
Useful for pagination indexes.

```ts
import { parseAsIndex } from 'nuqs'

const [pageIndex] = useQueryState('page', parseAsIndex.withDefault(0))
```

<Suspense fallback={<DemoFallback />}>
  <IndexParserDemo />
</Suspense>

## Boolean

```ts
import { parseAsBoolean } from 'nuqs'

useQueryState('bool', parseAsBoolean.withDefault(false))
```

<Suspense fallback={<DemoFallback />}>
  <BooleanParserDemo />
</Suspense>

## Literals

These parsers extend the basic integer and float parsers, but validate against
some expected values, defined as [TypeScript literals](https://www.typescriptlang.org/docs/handbook/2/everyday-types.html#literal-types).

```ts
import { parseAsStringLiteral, type inferParserType } from 'nuqs'

// Create parser
const parser = parseAsStringLiteral(['asc', 'desc'])

// Optional: extract the type
type SortOrder = inferParserType<typeof parser> // 'asc' | 'desc'
```

<Callout title="Should I declare values inline or outside the parser?">
  It depends®. Declaring them inline is shorter, and makes the parser
  the source of truth for type inference with `inferParserType{:ts}`,
  but it locks the values inside the parser.

  Declaring them outside allows reading and iterating over the values at runtime.
  Don't forget to add `as const{:ts}` though, otherwise the type will widen as a `string{:ts}`.
</Callout>

### String literals

```ts
// [!code word:as const]
import { parseAsStringLiteral } from 'nuqs'

// List accepted values
const sortOrder = ['asc', 'desc'] as const

// Then pass it to the parser
parseAsStringLiteral(sortOrder)
```

<Suspense fallback={<DemoFallback />}>
  <StringLiteralParserDemo />
</Suspense>

### Numeric literals

```ts
import { parseAsNumberLiteral } from 'nuqs'

parseAsNumberLiteral([1, 2, 3, 4, 5, 6])
```

```ts
// [!code word:as const]
import { parseAsNumberLiteral } from 'nuqs'

// List accepted values
const diceSides = [1, 2, 3, 4, 5, 6] as const

// Then pass it to the parser
parseAsNumberLiteral(diceSides)
```

## Enums

String enums are a bit more verbose than string literals, but `nuqs` supports them.

```ts
enum Direction {
  up = 'UP',
  down = 'DOWN',
  left = 'LEFT',
  right = 'RIGHT'
}

parseAsStringEnum<Direction>(Object.values(Direction))
```

<Callout title="Note">
  The query string value will be the **value** of the enum, not its name (here:
  `?direction=UP`).
</Callout>

## Dates & timestamps

There are three parsers that give you a `Date` object, their difference is
on how they encode the value into the query string.

### ISO 8601 Datetime

```ts
import { parseAsIsoDateTime } from 'nuqs'
```

<Suspense>
  <DatetimeISOParserDemo />
</Suspense>

### ISO 8601 Date

<FeatureSupportMatrix introducedInVersion='2.1.0' />

```ts
import { parseAsIsoDate } from 'nuqs'
```

<Suspense>
  <DateISOParserDemo />
</Suspense>

<Callout>
  The Date is parsed without the time zone offset, making it at 00:00:00 UTC.
</Callout>

### Timestamp

Miliseconds since the Unix epoch.

```ts
import { parseAsTimestamp } from 'nuqs'
```

<Suspense>
  <DateTimestampParserDemo />
</Suspense>

## Arrays

All of the parsers on this page can be used to parse arrays of their respective types.

```ts
import { parseAsArrayOf, parseAsInteger } from 'nuqs'

parseAsArrayOf(parseAsInteger)

// Optionally, customise the separator
parseAsArrayOf(parseAsInteger, ';')
```

## JSON

If primitive types are not enough, you can encode JSON in the query string.

Pass it a [Standard Schema](https://standardschema.dev) (eg: a Zod schema)
to validate and infer the type of the parsed data:

```ts
// [!code word:parseAsJson]
import { parseAsJson } from 'nuqs'
import { z } from 'zod'

const schema = z.object({
  pkg: z.string(),
  version: z.number(),
  worksWith: z.array(z.string())
})

// This parser is a function, don't forget to call it
// with your schema as an argument.
const [json, setJson] = useQueryState('json', parseAsJson(schema))

setJson({
  pkg: 'nuqs',
  version: 2,
  worksWith: ['Next.js', 'React', 'Remix', 'React Router', 'and more']
})
```

<Suspense>
  <JsonParserDemo />
</Suspense>

Using other validation libraries is possible: `parseAsJson{:ts}` accepts
any Standard Schema compatible input (eg: ArkType, Valibot),
or a custom validation function (eg: Yup, Joi, etc):

```ts
import { object, string, number } from 'yup';

let userSchema = object({
  name: string().required(),
  age: number().required().positive().integer(),
});

parseAsJson(userSchema.validateSync)
```

<Callout title="Note">
  Validation functions must either throw an error or
  return `null{:ts}` for invalid data. Only **synchronous** validation is supported.
</Callout>

## Native Arrays

<FeatureSupportMatrix introducedInVersion='2.7.0' />

If you want to use the native URL format for arrays, repeating the same key multiple times like:

import { Querystring } from '@/src/components/querystring'

<Querystring path="/products" value='?tag=books&tag=tech&tag=design' />

you can now use `MultiParsers{:ts}` like `parseAsNativeArrayOf{:ts}` to read and write those values in a fully type-safe way.

```tsx
import { useQueryState, parseAsNativeArrayOf, parseAsInteger } from 'nuqs'

const [projectIds, setProjectIds] = useQueryState(
  'project',
  parseAsNativeArrayOf(parseAsInteger)
)

// ?project=123&project=456 → [123, 456]
```

<Suspense fallback={<DemoFallback />}>
  <NativeArrayParserDemo />
</Suspense>

<Callout title="Note: empty array default">
  `parseAsNativeArrayOf{:ts}` has a built-in default value of empty array (`.withDefault([]){:ts}`) so that you don't have to handle `null{:ts}` cases.

  Calls to `.withDefault(){:ts}` can be chained, so you can use it to set a custom default.
</Callout>

## Using parsers server-side

For shared code that may be imported in the Next.js app router, you should import
parsers from `nuqs/server` to use them in both server & client code,
as it doesn't include the `'use client'{:ts}` directive.

```ts
import { parseAsString } from 'nuqs/server'
```

Importing from `nuqs` will only work in client code, and will throw bundling errors
when using functions (like `.withDefault{:ts}` & `.withOptions{:ts}`)
across shared code.

For all other frameworks, you can use either interchangeably, as they don't
care about the `'use client'{:ts}` directive.
